# _*_ coding:utf-8 _*_
# @File  : ruizy.py
# @Time  : 2021-07-13 15:16
# @Author: zizle
import json
import os
import datetime
import hashlib
from fastapi import APIRouter, Query, Path, Depends, Body, Header
from fastapi.responses import StreamingResponse
from pydantic import BaseModel

from hutool.security import generate_image_code, decrypt_access_token
from hutool.utils import list2tree, create_random_string
from v1_all_api.all_utils import datetime_utils
from db import RedisConnection, FAConnection
from interfaces.depends import admin_logged_require, logged_require
from category import (MENUS, EXCHANGE, VARIETY_GROUPS, CLIENTS, NO_REAL_VARIETY, NO_PRODUCT_VARIETY, CFFEX_VARIETY,
                      SPECIAL_VARIETY, NO_STRATEGY_VARIETY)
from status import r_status
from configs import APP_DIR

ruizy_api = APIRouter()


@ruizy_api.get('/pubkey/', summary='获取加密的公钥')  # 客户端开启时需要获取
async def get_rsa_public_key():
    pubkey_file = os.path.join(APP_DIR, 'ini/public0.pem')
    with open(pubkey_file, 'r') as fp:
        pubkey = fp.read()
    return {'code': r_status.SUCCESS, 'message': '获取rsa公钥成功!', 'pubkey': pubkey}


@ruizy_api.get('/image-code/', summary='获取图片验证码')
async def get_image_code(img_uuid: str = Query(...)):
    image_buf, text = generate_image_code()
    # 将验证码存入redis
    redis_conn = RedisConnection()
    redis_conn.set_value(key=f'{img_uuid}', value=text, expires=120)
    return StreamingResponse(content=image_buf)


@ruizy_api.get('/menu.{menu_type}/', summary='获取菜单')
async def get_system_menu(authorization: str = Header(''), menu_type: str = Path(...), cuuid: str = Query(''),
                          backend: bool = Query(False)):
    person = decrypt_access_token(authorization) if authorization else None
    db = FAConnection(conn_name='query menus')
    if menu_type == 'all':
        # 查询所有菜单
        sql = 'SELECT id,parent_id,category,icon,name_en,name_zh,is_inner,orient FROM sys_menu ' \
              'WHERE is_active=1 ORDER BY sorted;'
        records = db.query(sql)
        for item in records:
            item['category_text'] = MENUS.get(item['category'], '未知')
            item['orient'] = item['orient'].split(',') if item['orient'] else []
        menus = list2tree(records, 0)
        return {'code': r_status.SUCCESS, 'message': '获取所有菜单成功!', 'data': menus}

    elif menu_type == 'backadmin':
        sql = 'SELECT id,parent_id,category,icon,name_en,name_zh,is_inner,orient FROM sys_menu ' \
              'WHERE category=1 AND is_active=1 ORDER BY id,sorted;'
        records = db.query(sql)
        for item in records:
            item['category_text'] = MENUS.get(item['category'], '未知')
        menus = list2tree(records, 0)
        return {'code': r_status.SUCCESS, 'message': '获取后台管理菜单成功!', 'data': menus}
    elif menu_type == 'client':
        if not cuuid:
            db.close()
            return {'code': r_status.VALIDATE_ERROR, 'message': '无客户端识别码!', 'data': []}
        sql = 'SELECT id,parent_id,category,icon,name_en,name_zh,is_inner,orient FROM sys_menu ' \
              'WHERE category=2 AND is_active=1 ORDER BY sorted;'
        records = db.query(sql, keep_conn=True)

        today_str = datetime.datetime.today().strftime('%Y-%m-%d')
        # 查询客户端被屏蔽的菜单项
        ex_sql = 'SELECT id,menu_id FROM sys_client_exclude_menu WHERE client_code=%s AND expire>=%s;'
        exclude = db.query(ex_sql, [cuuid, today_str], keep_conn=True)
        exclude = [ex['menu_id'] for ex in exclude]
        # 过滤掉客户端排除的菜单
        records = list(filter(lambda x: x['id'] not in exclude, records))
        # 如果非管理端，过滤掉管理端的独有的菜单
        if not backend or not person:  # 非管理端，用户未登录，都去除管理端独有菜单
            records = list(filter(lambda x: x['is_inner'] == 0, records))
        if person and person['role'] in ['free', 'vip']:  # 用户已登录，但是是普通用户，则去除管理端独有的菜单
            records = list(filter(lambda x: x['is_inner'] == 0, records))
        reject_menus_id = [m['id'] for m in records]  # 无权限的id列表(即默认都无权限)
        if person:  # 用户已登录
            # if person['role'] in ['superuser', 'operator', 'research']:  # 非研究员
            #     reject_menus_id = []  # 默认都有权限
            # else:
            # 用户无权限的菜单
            reject_query_sql = 'SELECT * FROM user_user_reject_menu WHERE user_id=%s;'
            reject_menus = db.query(reject_query_sql, [person['uid']], keep_conn=True)
            # 过滤掉日期小于今天的
            today_str = datetime.datetime.today().strftime('%Y-%m-%d')
            reject_menus = list(filter(lambda x: x['expire'].strftime('%Y-%m-%d') >= today_str, reject_menus))
            reject_menus_id = [au_item['menu_id'] for au_item in reject_menus]
        db.close()
        # 处理数据
        for item in records:
            item['category_text'] = MENUS.get(item['category'], '未知')
            item['auth'] = 0 if item['id'] in reject_menus_id else 1
        menus = list2tree(records, 0)
        # if backend:  # 如果是管理端
        #     auth = 1 if person else 0
        #     back_admin_children = [{'id': -10, 'parent_id': -9, 'category': -9, 'icon': '', 'name_en': 'variety_lib',
        #                             'name_zh': '品种数据库', 'auth': auth, 'children': []},
        #                            {'id': -10, 'parent_id': -9, 'category': -9, 'icon': '', 'name_en': 'strategy_lib',
        #                             'name_zh': '交易策略管理', 'auth': auth, 'children': []},
        #                            {'id': -10, 'parent_id': -9, 'category': -9, 'icon': '', 'name_en': 'explan_lib',
        #                             'name_zh': '投资方案管理', 'auth': auth, 'children': []}]
        #     back_admin = {'id': -9, 'parent_id': 0, 'category': -9, 'icon': '', 'name_en': 'backend', 'name_zh': '后台管理',
        #                   'auth': auth, 'children': back_admin_children}
        #     menus.append(back_admin)
        # 添加关于系统菜单
        about_us_children = [{'id': -9, 'parent_id': -9, 'category': -9, 'icon': '', 'name_en': 'ruizy_information',
                              'name_zh': '系统简介', 'auth': 1, 'children': []},
                             {'id': -9, 'parent_id': -9, 'category': -9, 'icon': '', 'name_en': 'user_guide',
                              'name_zh': '使用手册', 'auth': 1, 'children': []},
                             {'id': -9, 'parent_id': -9, 'category': -9, 'icon': '', 'name_en': 'suggest_plat',
                              'name_zh': '交流反馈', 'auth': 1, 'children': []}
                             ]
        about_system = {'id': -9, 'parent_id': 0, 'category': -9, 'icon': '', 'name_en': 'about_system',
                        'name_zh': '关于系统', 'auth': 1, 'children': about_us_children}
        menus.append(about_system)
        # if cuuid == 'D82A9F7345A87D413861BF7559A18113':
        #     temp_children = [{'id': -99, 'icon': '', 'name_zh': '交易所数据', 'name_en': 'jyssj', 'auth': 1, 'children': []},
        #                      {'id': -99, 'icon': '', 'name_zh': '仓库管理', 'name_en': 'ckgl', 'auth': 1, 'children': []},
        #                      {'id': -99, 'icon': '', 'name_zh': '仓单数据', 'name_en': 'cdsj', 'auth': 1, 'children': []},
        #                      {'id': -99, 'icon': '', 'name_zh': '报告管理', 'name_en': 'zxgl', 'auth': 1, 'children': []},
        #                      {'id': -99, 'icon': '', 'name_zh': '广告管理', 'name_en': 'gggl', 'auth': 1, 'children': []}]
        #     menus.append({'id': -99, 'icon': '', 'name_zh': '临时使用', 'name_en': 'temp', 'auth': 1, 'children': temp_children})
        return {'code': r_status.SUCCESS, 'message': '获取客户端菜单成功', 'data': menus}
    else:
        return {'code': r_status.VALIDATE_ERROR, 'message': '没有此类别的相关菜单!', 'data': []}


@ruizy_api.get('/orient-menu/', summary='获取定向菜单')
async def get_orient_menu(person: dict = Depends(logged_require), p: str = Query(...)):
    # p: 定向菜单的父级name_en
    # 找到p的id
    db_conn = FAConnection()
    parent_obj = db_conn.query('SELECT id FROM sys_menu WHERE name_en=%s;', param=[p],
                               keep_conn=True, fetchone=True)[0]
    if not parent_obj:
        db_conn.close()
        return {'code': r_status.NOT_CONTENT, 'message': '关联菜单不存在', 'data': []}
    parent_sql = 'SELECT id,name_zh,name_en,orient FROM sys_menu WHERE parent_id=%s;'
    records = db_conn.query(parent_sql, param=[parent_obj['id']])
    response_menu = []

    # is_passed = [True for ac in person['access'] if ac in ['superuser', 'operator', 'backendAdmin']]
    is_passed = person['role_code'] in [9999, 9000]
    for m_item in records:
        orient = m_item['orient'].split(',')
        # 如果是后台管理员
        if is_passed:
            response_menu.append(m_item)
        else:
            # 如果用户ID在里面，添加
            if str(person['uid']) in orient:
                response_menu.append(m_item)
    return {'code': r_status.SUCCESS,  'message': '查询定向菜单成功', 'data': response_menu}


class SystemMenuItem(BaseModel):
    parent_id: int
    name_en: str
    name_zh: str
    icon: str
    category: int
    is_inner: int = 0


@ruizy_api.post('/menu/', summary='增加一个菜单')
async def create_menu(person: dict = Depends(admin_logged_require), menu_item: SystemMenuItem = Body(...)):
    user_id = person['uid']
    db = FAConnection()
    # 查询数量
    count_obj = db.query('SELECT count(id) AS total_count FROM sys_menu;', keep_conn=True)
    sort_index = count_obj[0]['total_count']
    sql = 'INSERT INTO sys_menu (creator,parent_id,category,name_en,name_zh,icon,is_inner,sorted) ' \
          'VALUES (%s,%s,%s,%s,%s,%s,%s,%s);'
    params = [user_id, menu_item.parent_id, menu_item.category, menu_item.name_en, menu_item.name_zh, menu_item.icon,
              menu_item.is_inner,sort_index]
    count, _ = db.insert(sql, params)
    if count:
        return {'code': r_status.CREATED_SUCCESS, 'message': '创建菜单成功!'}
    return {'code': r_status.SERVER_ERROR, 'message': '创建菜单失败了!'}


@ruizy_api.put('/menu/{menu_id}/', summary='修改一个菜单信息')
async def update_menu(person: dict = Depends(admin_logged_require), menu_id: int = Path(..., ge=1),
                      menu_item: dict = Body(...)):
    # 含有的字段
    include_columns = ['parent_id', 'category', 'icon', 'name_en', 'name_zh', 'sorted', 'is_active', 'is_inner',
                       'orient']
    exclude_keys = list(filter(lambda x: x not in include_columns, menu_item.keys()))
    for k in exclude_keys:
        del menu_item[k]
    # 更新字段
    update_sql = 'UPDATE sys_menu SET '
    for k, v in menu_item.items():
        update_sql += f'{k}=%({k})s,'
    update_sql = update_sql[:-1] + ' WHERE id=%(id)s LIMIT 1;'
    menu_item['id'] = menu_id

    db = FAConnection(conn_name='修改菜单状态')
    if 'orient' in menu_item.keys():
        menu_item['orient'] = ','.join(list(set(menu_item['orient'].split(','))))  # 去重

    if 'is_inner' in menu_item.keys():
        # 修改子菜单的内部状态
        c_sql = 'UPDATE sys_menu SET is_inner=%s WHERE parent_id=%s;'
        c_param = [menu_item['is_inner'], menu_id]
        success = db.execute_tasks(sql_list=[update_sql, c_sql], param_list=[menu_item, c_param])
    else:
        _, success = db.execute(update_sql, param=menu_item)
    if success:
        return {'code': r_status.SUCCESS, 'message': '修改状态成功!'}
    return {'code': r_status.UNKNOWN_ERROR, 'message': '修改状态失败了!'}


@ruizy_api.get('/variety/', summary='查询系统所有品种')
async def get_sys_variety(state: int = Query(0, ge=0, le=4)):
    # state=0获取所有，state=1获取实际交易，state=2获取商品品种, state=3中金所的品种变为股指,国债,宏观,外汇
    db = FAConnection(conn_name='查询系统品种')
    query_sql = 'SELECT id,variety_en,variety_name,exchange,category,price_unit,hand_amount,hand_unit,' \
                'strategy_fund,bail_rate,is_active ' \
                'FROM sys_variety WHERE is_active=1;'
    records = db.query(query_sql)
    if state == 1:  # 只获取真正交易的品种
        records = list(filter(lambda x: x['variety_en'] not in NO_REAL_VARIETY, records))
    if state == 2:  # 只获取商品类的品种
        records = list(filter(lambda x: x['variety_en'] not in NO_PRODUCT_VARIETY, records))
    if state == 3:  # 中金所品种转为股指,国债,宏观,外汇
        records = list(filter(lambda x: x['variety_en'] not in CFFEX_VARIETY, records))
    if state == 4:  # 获取交易策略使用的品种
        records = list(filter(lambda x: x['variety_en'] not in NO_STRATEGY_VARIETY, records))
    for item in records:
        item['category_name'] = VARIETY_GROUPS.get(item['category'], '未知')
        item['exchange_name'] = EXCHANGE.get(item['exchange'], '未知')
    return {'code': r_status.SUCCESS, 'message': '查询系统品种成功', 'data': records}


@ruizy_api.get('/latest-contracts/', summary='获取某个品种所有合约')
async def get_contracts(variety: str = Query(...)):
    if variety in SPECIAL_VARIETY.keys():
        # 生成近一年的合约
        today = datetime.datetime.today()
        finish_day = today + datetime.timedelta(days=365)
        cache_contract = []
        while today < finish_day:
            c = today.strftime('%Y%m')[2:]
            if c not in cache_contract:
                cache_contract.append(c)
            today += datetime.timedelta(days=20)
        resp_contract = [{'variety_en': variety, 'contract': item} for item in cache_contract]
        return {'code': r_status.SUCCESS, 'message': '获取合约成功!', 'data': resp_contract}
    # 查询品种的合约
    db_conn = FAConnection(conn_name='查询合约')
    query_ = datetime.datetime.today() + datetime.timedelta(days=-1)
    query_date = datetime_utils.exchange_date_nearby(date_node=query_, f='%Y%m%d')
    sql = "SELECT variety_en,lcontract FROM ruizy_variety_contract WHERE variety_en=%s AND contract_date=%s;"
    prm = (variety, query_date)
    records = db_conn.query(sql, prm)
    for item in records:
        item['quotes_date'] = datetime.datetime.today().strftime('%Y-%m-%d')
        item['contract'] = item['variety_en'] + item['lcontract']
    records.sort(key=lambda x: x['contract'])
    return {'code': r_status.SUCCESS, 'message': '获取合约成功!', 'data': records}


@ruizy_api.get('/contract/{contract}/', summary='获取某个合约的详细行情信息')
async def get_contract_detail(date: str = Query(...), contract: str = Path(...)):
    try:
        d = datetime.datetime.strptime(date, '%Y-%m-%d').timestamp()
    except ValueError:
        return {'code': r_status.VALIDATE_ERROR, 'message': '参数错误!'}
    sql = 'SELECT id,variety_en,contract,quotes_ts,open_price,close_price ' \
          'FROM dat_futures_daily_quotes WHERE quotes_ts=%s AND contract=%s;'
    db = FAConnection(conn_name='查询合约行情')
    item = db.query(sql, param=[d, contract], fetchone=True)[0]
    return {'code': r_status.SUCCESS, 'message': '成功!', 'data': item}


@ruizy_api.get('/users/', summary='查询指定类型的所有用户')
async def get_pointer_role_users(role: str = Query(...)):
    if role not in ['superuser', 'operator', 'research', 'normal']:
        return {'code': r_status.SUCCESS, 'message': '查询用户成功!', 'data': []}
    sql = 'SELECT id,user_code,username,phone,email,role,note,admin_name FROM user_user WHERE role=%s AND is_active=1;'
    db = FAConnection(conn_name='查询指定角色所有用户')
    records = db.query(sql, [role])
    return {'code': r_status.SUCCESS, 'message': '查询指定角色所有用成功!', 'data': records}


# 通过client_code查询客户端
@ruizy_api.get('/client/', summary='通过uuid查询客户端')
async def query_client(uuid: str = Query(..., min_length=15, max_length=32)):
    sql = 'SELECT client_name,client_code,resolution FROM sys_client WHERE client_code like %s;'
    db = FAConnection(conn_name='UUID查询客户端')
    records = db.query(sql, param=[f'%{uuid}%'])
    return {'code': r_status.SUCCESS, 'message': '查询成功!', 'data': records}


# 创建客户端
@ruizy_api.post('/client/', summary='新建一个客户端')
async def initial_client(data: dict = Body(...)):
    disk = data.get('disk', None)
    board = data.get('board', None)
    category = data.get('category', 2)  # 默认2为普通客户端
    resolution = data.get('resolution', '')  # 分辨率
    if not disk:
        disk = 'None'
    if not board:
        board = 'None'
    if not all([disk, board]):
        return {'code': r_status.VALIDATE_ERROR, 'message': '缺少参数！', 'uuid': ''}
    if category not in CLIENTS.keys():
        return {'code': r_status.VALIDATE_ERROR, 'message': '客户端类型错误！', 'uuid': ''}
    # 哈希创建客户端
    hash_handler = hashlib.md5(disk.encode('utf8'))  # 硬盘
    hash_handler.update(board.encode('utf8'))  # 主板
    hash_handler.update(str(category).encode('utf8'))  # 类型
    client_code = hash_handler.hexdigest().upper()
    client_name = 'C_' + create_random_string(10)

    db = FAConnection(conn_name='初始化客户端')
    # 查询不存在再增加(使用insert ignore会导致自增id不连续的问题)
    query_sql = 'SELECT id,is_active FROM sys_client WHERE client_code=%s;'
    clients = db.query(query_sql, [client_code], fetchone=True, keep_conn=True)
    client_obj = clients[0]
    if client_obj:  # 存在
        # 检测是否有效
        if not client_obj['is_active']:
            return {'code': r_status.FORBIDDEN, 'message': '客户端无效!', 'uuid': client_code}
        # 更新类型与开启时间和分辨率
        success = db.execute('UPDATE sys_client SET update_time=%s,category=%s,resolution=%s WHERE id=%s LIMIT 1;',
                             [datetime.datetime.now(), category, resolution, client_obj['id']])
        if success:
            return {'code': r_status.SUCCESS, 'message': '初始化客户端成功!', 'uuid': client_code}
        return {'code': r_status.SERVER_ERROR, 'message': '初始化客户端失败!', 'uuid': ''}
    # 新增
    sql = 'INSERT IGNORE INTO sys_client (client_name,disk,board,client_code,category,resolution) ' \
          'VALUES (%s,%s,%s,%s,%s,%s);'
    params = [client_name, disk, board, client_code, category, resolution]
    db.insert(sql, params)
    return {'code': r_status.SUCCESS, 'message': '初始化客户端成功!', 'uuid': client_code}


# 查询客户端更新信息
@ruizy_api.get('/version/', summary='查询版本信息')
async def get_client_version(v: str = Query(...), cuuid: str = Query(...)):
    # need_update = False if v == '1.1' else True
    # 查询客户端分类
    query_sql = 'SELECT id,category FROM sys_client WHERE client_code=%s LIMIT 1;'
    db = FAConnection(conn_name='查询客户端分类更新')
    clients = db.query(query_sql, [cuuid])
    if clients and clients[0]['category'] == 1:
        prefix_folder = 'IN'
    else:
        prefix_folder = 'OUT'
    # 读取当前内部和外部最新版本号
    version_file = os.path.join(APP_DIR, 'conf/release.json')
    with open(version_file, 'r') as fp:
        release_opt = json.load(fp)
    latest_version = release_opt.get(prefix_folder, '')
    files = release_opt.get(f'{prefix_folder}_FILES', [])
    need_update = False
    if v != latest_version:
        need_update = True
    update_files = [f'{prefix_folder}/{f}' for f in files]
    # 更新资源的zip文件,放到静态文件夹下的`versions`下的版本号文件夹内，命名为files里的样式
    return {'code': r_status.SUCCESS, 'message': '检测版本成功', 'need_update': need_update,
            'version': latest_version, 'files': update_files}


# 获取系统介绍
@ruizy_api.get('/introduction/', summary='获取系统介绍')
async def get_system_introduction():
    intro = """
    <div>
    <div style="font-weight: 700;margin-top:18px">【研究院简介】</div>
    <div style="text-indent:28px;line-height:25px;margin: 0 10px">
      瑞达期货研究院成立于2003年，是业内较早的期货研究机构，在业内具有较高的知名度和较强的影响力。
      多次获大商所全国十大研发团队称号，并多次在和讯、中金在线、期货日报等媒体评选“最佳研究院”、“优势品种分析师”等称号。
      研究范围覆盖宏观经济、金融、金属、能源化工、农产品、套利、产品开发等相关领域，对内建立了一套功能强大的宏观、
      行业以及商品数据分析系统，对外与相关行业协会、期交所以及中国行业信息咨询机构保持良好的交流与合作，
      研究成果在《中国证券报》、《上海证券报》、《期货日报》、《投资有道》、《中国黄金报》、和讯网、全景网、金融界、
      新浪网等核心财经媒体均有发表。
    </div>
    <div style="font-weight: 700;margin-top:18px">【系统简介】</div>
    <div style="text-indent: 28px;line-height:25px;margin: 0 10px">
      瑞智分析决策系统是瑞达研究院自主研发的一套集期货资讯和数据为一体的期货分析决策助手。系统从几大功能板块出发，提供研究院最新
      的研究资讯内容。品种数据库是经专业品种研究人员搜集二次加工处理后的数据，针对期货的研究提供了更清晰可用的数据。
    </div>
    <div style="font-weight: 700;margin-top:18px">【免责声明】</div>
    <div style="text-indent:28px;line-height:25px;margin: 0 10px">
     软件使用过程中产生任何问题、任何通过本软件获得的内容，本软件的作者和发行方对其合法性概不负责，
     亦不承担任何法律责任。任何单位、或个人认为本软件提供的内容可能涉嫌侵犯其合法权益，
     应该及时向本软件作者反馈详细侵权情况，我们在收到证明侵权内容的证明后，将会尽快移除被控侵权内容。 
     本软件不保证能持续提供服务，在后续版本升级过程中，旧版本有可能停止服务。
    </div>
    <div style="text-indent:28px;line-height:25px;margin: 0 10px">
     本软件力求内容信息准确可靠，但对这些信息的准确性及完整性不做任何保证，据此投资，责任自负。
     本软件提供的内容不构成个人投资建议，客户应考虑本软件中的任何意见或建议是否符合其特定状况。
     报告版权仅为我公司所有，未经书面许可，任何机构和个人不得以任何形式翻版、复制和发布。
     如引用、刊发，需注明出处为瑞达期货股份有限公司研究院，且不得对本报告进行有悖原意的引用、删节和修改。
    </div>
    </div>
    """
    return {'code': r_status.SUCCESS, 'message': '获取系统介绍成功!', 'data': intro}